1use super::bse::*;
3use super::dsc::*;
4use crate::ext::io::*;
5use crate::ext::mutex::*;
6use crate::scripts::base::*;
7use crate::types::*;
8use crate::utils::encoding::encode_string;
9use crate::utils::struct_pack::*;
10use crate::utils::threadpool::*;
11use anyhow::Result;
12use msg_tool_macro::*;
13use std::collections::HashMap;
14use std::io::{Read, Seek, SeekFrom, Write};
15use std::ops::DerefMut;
16use std::sync::{Arc, Mutex};
17
18#[derive(Debug)]
19pub struct BgiArchiveBuilder {}
21
22impl BgiArchiveBuilder {
23 pub const fn new() -> Self {
25 BgiArchiveBuilder {}
26 }
27}
28
29impl ScriptBuilder for BgiArchiveBuilder {
30 fn default_encoding(&self) -> Encoding {
31 Encoding::Cp932
32 }
33
34 fn default_archive_encoding(&self) -> Option<Encoding> {
35 Some(Encoding::Cp932)
36 }
37
38 fn build_script(
39 &self,
40 data: Vec<u8>,
41 filename: &str,
42 _encoding: Encoding,
43 archive_encoding: Encoding,
44 config: &ExtraConfig,
45 _archive: Option<&Box<dyn Script>>,
46 ) -> Result<Box<dyn Script + Send + Sync>> {
47 Ok(Box::new(BgiArchive::new(
48 MemReader::new(data),
49 archive_encoding,
50 config,
51 filename,
52 )?))
53 }
54
55 fn build_script_from_file(
56 &self,
57 filename: &str,
58 _encoding: Encoding,
59 archive_encoding: Encoding,
60 config: &ExtraConfig,
61 _archive: Option<&Box<dyn Script>>,
62 ) -> Result<Box<dyn Script + Send + Sync>> {
63 if filename == "-" {
64 let data = crate::utils::files::read_file(filename)?;
65 Ok(Box::new(BgiArchive::new(
66 MemReader::new(data),
67 archive_encoding,
68 config,
69 filename,
70 )?))
71 } else {
72 let f = std::fs::File::open(filename)?;
73 let reader = std::io::BufReader::new(f);
74 Ok(Box::new(BgiArchive::new(
75 reader,
76 archive_encoding,
77 config,
78 filename,
79 )?))
80 }
81 }
82
83 fn build_script_from_reader<'a>(
84 &self,
85 reader: Box<dyn ReadSeek + Send + Sync + 'a>,
86 filename: &str,
87 _encoding: Encoding,
88 archive_encoding: Encoding,
89 config: &ExtraConfig,
90 _archive: Option<&Box<dyn Script>>,
91 ) -> Result<Box<dyn Script + Send + Sync + 'a>> {
92 Ok(Box::new(BgiArchive::new(
93 reader,
94 archive_encoding,
95 config,
96 filename,
97 )?))
98 }
99
100 fn extensions(&self) -> &'static [&'static str] {
101 &["arc"]
102 }
103
104 fn script_type(&self) -> &'static ScriptType {
105 &ScriptType::BGIArcV1
106 }
107
108 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
109 if buf_len >= 12 && buf.starts_with(b"PackFile ") {
110 return Some(10);
111 }
112 None
113 }
114
115 fn is_archive(&self) -> bool {
116 true
117 }
118
119 fn create_archive(
120 &self,
121 filename: &str,
122 files: &[&str],
123 encoding: Encoding,
124 config: &ExtraConfig,
125 ) -> Result<Box<dyn Archive>> {
126 let f = std::fs::File::create(filename)?;
127 let writer = std::io::BufWriter::new(f);
128 Ok(Box::new(BgiArchiveWriter::new(
129 writer, files, encoding, config,
130 )?))
131 }
132}
133
134#[derive(Clone, Debug, StructPack, StructUnpack)]
135struct BgiFileHeader {
136 #[fstring = 16]
137 filename: String,
138 offset: u32,
139 size: u32,
140 #[fvec = 8]
141 _padding: Vec<u8>,
142}
143
144#[derive(Debug)]
145struct Entry<T: Read + Seek + std::fmt::Debug> {
146 header: BgiFileHeader,
147 reader: Arc<Mutex<T>>,
148 pos: usize,
149 base_offset: u64,
150 script_type: Option<ScriptType>,
151}
152
153impl<T: Read + Seek + std::fmt::Debug + Send + Sync> ArchiveContent for Entry<T> {
154 fn name(&self) -> &str {
155 &self.header.filename
156 }
157
158 fn size(&self) -> Option<u64> {
159 Some(self.header.size as u64)
160 }
161
162 fn script_type(&self) -> Option<&ScriptType> {
163 self.script_type.as_ref()
164 }
165
166 fn to_data<'a>(&'a mut self) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
167 Ok(Box::new(self))
168 }
169}
170
171impl<T: Read + Seek + std::fmt::Debug> Read for Entry<T> {
172 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
173 let mut reader = self.reader.lock().map_err(|e| {
174 std::io::Error::new(
175 std::io::ErrorKind::Other,
176 format!("Failed to lock mutex: {}", e),
177 )
178 })?;
179 reader.seek(SeekFrom::Start(
180 self.base_offset + self.header.offset as u64 + self.pos as u64,
181 ))?;
182 let bytes_read = buf.len().min(self.header.size as usize - self.pos);
183 if bytes_read == 0 {
184 return Ok(0);
185 }
186 let bytes_read = reader.read(&mut buf[..bytes_read])?;
187 self.pos += bytes_read;
188 Ok(bytes_read)
189 }
190}
191
192impl<T: Read + Seek + std::fmt::Debug> Seek for Entry<T> {
193 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
194 let new_pos = match pos {
195 SeekFrom::Start(offset) => offset as usize,
196 SeekFrom::End(offset) => {
197 if offset < 0 {
198 if (-offset) as usize > self.header.size as usize {
199 return Err(std::io::Error::new(
200 std::io::ErrorKind::InvalidInput,
201 "Seek from end exceeds file length",
202 ));
203 }
204 self.header.size as usize - (-offset) as usize
205 } else {
206 self.header.size as usize + offset as usize
207 }
208 }
209 SeekFrom::Current(offset) => {
210 if offset < 0 {
211 if (-offset) as usize > self.pos {
212 return Err(std::io::Error::new(
213 std::io::ErrorKind::InvalidInput,
214 "Seek from current exceeds current position",
215 ));
216 }
217 self.pos.saturating_sub((-offset) as usize)
218 } else {
219 self.pos + offset as usize
220 }
221 }
222 };
223 self.pos = new_pos;
224 Ok(self.pos as u64)
225 }
226
227 fn stream_position(&mut self) -> std::io::Result<u64> {
228 Ok(self.pos as u64)
229 }
230}
231
232#[derive(Debug)]
233pub struct BgiArchive<'a, T: Read + Seek + std::fmt::Debug + 'a> {
235 reader: Arc<Mutex<T>>,
236 entries: Vec<BgiFileHeader>,
237 base_offset: u64,
238 #[cfg(feature = "bgi-img")]
239 is_sysgrp_arc: bool,
240 _mark: std::marker::PhantomData<&'a ()>,
241}
242
243impl<'b, T: Read + Seek + std::fmt::Debug + 'b> BgiArchive<'b, T> {
244 pub fn new(
251 mut reader: T,
252 archive_encoding: Encoding,
253 _config: &ExtraConfig,
254 _filename: &str,
255 ) -> Result<Self> {
256 let mut header = [0u8; 12];
257 reader.read_exact(&mut header)?;
258 if !header.starts_with(b"PackFile ") {
259 return Err(anyhow::anyhow!("Invalid BGI archive header"));
260 }
261
262 let file_count = reader.read_u32()?;
263 let mut entries = Vec::with_capacity(file_count as usize);
264 for _ in 0..file_count {
265 let entry = BgiFileHeader::unpack(&mut reader, false, archive_encoding, &None)?;
266 entries.push(entry);
267 }
268
269 #[cfg(feature = "bgi-img")]
270 let is_sysgrp_arc = _config.bgi_is_sysgrp_arc.unwrap_or_else(|| {
271 std::path::Path::new(&_filename.to_lowercase())
272 .file_name()
273 .map(|f| f == "sysgrp.arc")
274 .unwrap_or(false)
275 });
276
277 Ok(BgiArchive {
278 reader: Arc::new(Mutex::new(reader)),
279 entries,
280 base_offset: 16 + (file_count as u64 * 32),
281 #[cfg(feature = "bgi-img")]
282 is_sysgrp_arc,
283 _mark: std::marker::PhantomData,
284 })
285 }
286}
287
288impl<'b, T: Read + Seek + std::fmt::Debug + Send + Sync + 'b> Script for BgiArchive<'b, T> {
289 fn default_output_script_type(&self) -> OutputScriptType {
290 OutputScriptType::Json
291 }
292
293 fn default_format_type(&self) -> FormatOptions {
294 FormatOptions::None
295 }
296
297 fn is_archive(&self) -> bool {
298 true
299 }
300
301 fn iter_archive_filename<'a>(
302 &'a self,
303 ) -> Result<Box<dyn Iterator<Item = Result<String>> + 'a>> {
304 Ok(Box::new(
305 self.entries.iter().map(|e| Ok(e.filename.clone())),
306 ))
307 }
308
309 fn iter_archive_offset<'a>(&'a self) -> Result<Box<dyn Iterator<Item = Result<u64>> + 'a>> {
310 Ok(Box::new(self.entries.iter().map(|e| Ok(e.offset as u64))))
311 }
312
313 fn open_file<'a>(&'a self, index: usize) -> Result<Box<dyn ArchiveContent + Send + Sync + 'a>> {
314 if index >= self.entries.len() {
315 return Err(anyhow::anyhow!(
316 "Index out of bounds: {} (max: {})",
317 index,
318 self.entries.len()
319 ));
320 }
321 let entry = &self.entries[index];
322 let mut entry = Entry {
323 header: entry.clone(),
324 reader: self.reader.clone(),
325 pos: 0,
326 base_offset: self.base_offset,
327 script_type: None,
328 };
329 let mut buf = [0u8; 32];
330 match entry.read(&mut buf) {
331 Ok(_) => {}
332 Err(e) => {
333 return Err(anyhow::anyhow!(
334 "Failed to read entry '{}': {}",
335 entry.header.filename,
336 e
337 ));
338 }
339 }
340 entry.pos = 0;
341 if buf.starts_with(b"DSC FORMAT 1.00") {
342 let data = match entry.data() {
343 Ok(data) => data,
344 Err(e) => {
345 return Err(anyhow::anyhow!(
346 "Failed to read DSC data for '{}': {}",
347 entry.header.filename,
348 e
349 ));
350 }
351 };
352 entry.pos = 0;
353 let dsc = match DscDecoder::new(&data) {
354 Ok(dsc) => dsc,
355 Err(e) => {
356 return Err(anyhow::anyhow!(
357 "Failed to create DSC decoder for '{}': {}",
358 entry.header.filename,
359 e
360 ));
361 }
362 };
363 let decoded = match dsc.unpack() {
364 Ok(decoded) => decoded,
365 Err(e) => {
366 return Err(anyhow::anyhow!(
367 "Failed to unpack DSC data for '{}': {}",
368 entry.header.filename,
369 e
370 ));
371 }
372 };
373 let reader = MemReader::new(decoded);
374 if reader.data.starts_with(b"BSE 1.") {
375 match BseReader::new(reader, detect_script_type, &entry.header.filename) {
376 Ok(bse_reader) => {
377 return Ok(Box::new(bse_reader));
378 }
379 Err(e) => {
380 return Err(anyhow::anyhow!(
381 "Failed to create BSE reader for '{}': {}",
382 entry.header.filename,
383 e
384 ));
385 }
386 };
387 }
388 return Ok(Box::new(MemEntry {
389 name: entry.header.filename.clone(),
390 data: reader,
391 #[cfg(feature = "bgi-img")]
392 detect: if self.is_sysgrp_arc {
393 detect_script_type_sysgrp
394 } else {
395 detect_script_type
396 },
397 #[cfg(not(feature = "bgi-img"))]
398 detect: detect_script_type,
399 }));
400 }
401 if buf.starts_with(b"BSE 1.") {
402 let filename = entry.header.filename.clone();
403 #[cfg(feature = "bgi-img")]
404 let detect = if self.is_sysgrp_arc {
405 detect_script_type_sysgrp
406 } else {
407 detect_script_type
408 };
409 #[cfg(not(feature = "bgi-img"))]
410 let detect = detect_script_type;
411 match BseReader::new(entry, detect, &filename) {
412 Ok(mut bse_reader) => {
413 if bse_reader.is_dsc() {
414 let data = match bse_reader.data() {
415 Ok(data) => data,
416 Err(e) => {
417 return Err(anyhow::anyhow!(
418 "Failed to read BSE data for '{}': {}",
419 &filename,
420 e
421 ));
422 }
423 };
424 let dsc = match DscDecoder::new(&data) {
425 Ok(dsc) => dsc,
426 Err(e) => {
427 return Err(anyhow::anyhow!(
428 "Failed to create DSC decoder for '{}': {}",
429 &filename,
430 e
431 ));
432 }
433 };
434 let decoded = match dsc.unpack() {
435 Ok(decoded) => decoded,
436 Err(e) => {
437 return Err(anyhow::anyhow!(
438 "Failed to unpack DSC data for '{}': {}",
439 &filename,
440 e
441 ));
442 }
443 };
444 let reader = MemReader::new(decoded);
445 return Ok(Box::new(MemEntry {
446 name: filename,
447 data: reader,
448 detect,
449 }));
450 }
451 return Ok(Box::new(bse_reader));
452 }
453 Err(e) => {
454 return Err(anyhow::anyhow!(
455 "Failed to create BSE reader for '{}': {}",
456 &filename,
457 e
458 ));
459 }
460 };
461 }
462 #[cfg(feature = "bgi-img")]
463 if self.is_sysgrp_arc {
464 entry.script_type = Some(ScriptType::BGIImg);
465 } else {
466 entry.script_type =
467 detect_script_type(&buf, buf.len(), &entry.header.filename).cloned();
468 }
469 #[cfg(not(feature = "bgi-img"))]
470 {
471 entry.script_type =
472 detect_script_type(&buf, buf.len(), &entry.header.filename).cloned();
473 }
474 Ok(Box::new(entry))
475 }
476}
477
478struct MemEntry<F: Fn(&[u8], usize, &str) -> Option<&'static ScriptType>> {
479 name: String,
480 data: MemReader,
481 detect: F,
482}
483
484impl<F: Fn(&[u8], usize, &str) -> Option<&'static ScriptType>> Read for MemEntry<F> {
485 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
486 self.data.read(buf)
487 }
488}
489
490impl<F: Fn(&[u8], usize, &str) -> Option<&'static ScriptType>> ArchiveContent for MemEntry<F> {
491 fn name(&self) -> &str {
492 &self.name
493 }
494
495 fn size(&self) -> Option<u64> {
496 Some(self.data.data.len() as u64)
497 }
498
499 fn script_type(&self) -> Option<&ScriptType> {
500 (self.detect)(&self.data.data, self.data.data.len(), &self.name)
501 }
502
503 fn data(&mut self) -> Result<Vec<u8>> {
504 Ok(self.data.data.clone())
505 }
506
507 fn to_data<'a>(&'a mut self) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
508 Ok(Box::new(&mut self.data))
509 }
510}
511
512fn detect_script_type(buf: &[u8], buf_len: usize, filename: &str) -> Option<&'static ScriptType> {
513 if buf_len >= 28 && buf.starts_with(b"BurikoCompiledScriptVer1.00\0") {
514 return Some(&ScriptType::BGI);
515 }
516 #[cfg(feature = "bgi-img")]
517 if buf_len >= 16 && buf.starts_with(b"CompressedBG___") {
518 return Some(&ScriptType::BGICbg);
519 }
520 #[cfg(feature = "bgi-audio")]
521 if buf_len >= 8 && buf[4..].starts_with(b"bw ") {
522 return Some(&ScriptType::BGIAudio);
523 }
524 let filename = filename.to_lowercase();
525 if filename.ends_with("._bp") {
526 return Some(&ScriptType::BGIBp);
527 } else if filename.ends_with("._bsi") {
528 return Some(&ScriptType::BGIBsi);
529 }
530 None
531}
532
533#[cfg(feature = "bgi-img")]
534fn detect_script_type_sysgrp(
535 _buf: &[u8],
536 _buf_len: usize,
537 _filename: &str,
538) -> Option<&'static ScriptType> {
539 Some(&ScriptType::BGIImg)
540}
541
542pub struct BgiArchiveWriter<T: Write + Seek> {
544 writer: Arc<Mutex<T>>,
545 headers: Arc<Mutex<HashMap<String, BgiFileHeader>>>,
546 compress_file: bool,
547 encoding: Encoding,
548 compress_level: u8,
549 runner: ThreadPool<Result<()>>,
550}
551
552impl<T: Write + Seek> BgiArchiveWriter<T> {
553 pub fn new(
560 mut writer: T,
561 files: &[&str],
562 encoding: Encoding,
563 config: &ExtraConfig,
564 ) -> Result<Self> {
565 writer.write_all(b"PackFile ")?;
566 let file_count = files.len() as u32;
567 writer.write_u32(file_count)?;
568 let mut headers = HashMap::new();
569 for file in files {
570 let header = BgiFileHeader {
571 filename: file.to_string(),
572 offset: 0,
573 size: 0,
574 _padding: vec![0; 8],
575 };
576 header.pack(&mut writer, false, encoding, &None)?;
577 headers.insert(file.to_string(), header);
578 }
579 Ok(BgiArchiveWriter {
580 writer: Arc::new(Mutex::new(writer)),
581 headers: Arc::new(Mutex::new(headers)),
582 compress_file: config.bgi_compress_file,
583 encoding,
584 compress_level: config.bgi_compress_level,
585 runner: ThreadPool::new(
586 if config.bgi_compress_file {
587 config.bgi_arc_workers
588 } else {
589 1
590 },
591 Some("bgi-arc-writer"),
592 false,
593 )?,
594 })
595 }
596}
597
598impl<T: Write + Seek + Send + Sync + 'static> Archive for BgiArchiveWriter<T> {
599 fn new_file<'a>(
600 &'a mut self,
601 name: &str,
602 size: Option<u64>,
603 ) -> Result<Box<dyn WriteSeek + 'a>> {
604 let mut entry = self
605 .headers
606 .lock_blocking()
607 .get(name)
608 .ok_or_else(|| anyhow::anyhow!("File '{}' not found in archive", name))?
609 .clone();
610 if self.compress_file {
611 let inner = self.new_file_non_seek(name, size)?;
612 Ok(Box::new(Writer {
613 inner,
614 mem: MemWriter::new(),
615 }))
616 } else {
617 let mut writer = self.writer.lock_blocking();
618 let offset = writer.seek(SeekFrom::End(0))?;
619 entry.offset = offset as u32;
620 Ok(Box::new(BgiArchiveFile {
621 header: entry,
622 writer: self.writer.clone(),
623 pos: 0,
624 headers: self.headers.clone(),
625 name: name.to_owned(),
626 }))
627 }
628 }
629
630 fn new_file_non_seek<'a>(
631 &'a mut self,
632 name: &str,
633 _size: Option<u64>,
634 ) -> Result<Box<dyn Write + 'a>> {
635 if !self.compress_file {
636 return Ok(Box::new(self.new_file(name, _size)?));
637 }
638 for err in self.runner.take_results() {
639 err?;
640 }
641 let mut entry = self
642 .headers
643 .lock_blocking()
644 .get(name)
645 .ok_or_else(|| anyhow::anyhow!("File '{}' not found in archive", name))?
646 .clone();
647 let (reader, writer) = std::io::pipe()?;
648 let file = self.writer.clone();
649 let headers = self.headers.clone();
650 let compress_level = self.compress_level;
651 let name = name.to_owned();
652 self.runner.execute(
653 move |_| {
654 let mut reader = reader;
655 let mut data = Vec::new();
656 reader.read_to_end(&mut data)?;
657 let mut buf = MemWriter::new();
658 {
659 let mut b = std::io::BufWriter::new(&mut buf);
660 DscEncoder::new(&mut b, compress_level).pack(&data)?;
661 }
662 let mut writer = file.lock_blocking();
663 let offset = writer.seek(SeekFrom::End(0))?;
664 entry.offset = offset as u32;
665 writer.write_all(&buf.data)?;
666 entry.size = buf.data.len() as u32;
667 headers.lock_blocking().insert(name, entry);
668 Ok(())
669 },
670 true,
671 )?;
672 Ok(Box::new(writer))
673 }
674
675 fn write_header(&mut self) -> Result<()> {
676 self.runner.join();
677 for err in self.runner.take_results() {
678 err?;
679 }
680 let mut writer = self.writer.lock_blocking();
681 let mut headers = self.headers.lock_blocking();
682 writer.seek(SeekFrom::Start(0x10))?;
683 let base_offset = headers.len() as u32 * 0x20 + 16;
684 let mut files = headers.iter_mut().map(|(_, d)| d).collect::<Vec<_>>();
685 files.sort_by_key(|f| f.offset);
686 for file in files {
687 file.offset -= base_offset;
688 file.pack(writer.deref_mut(), false, self.encoding, &None)?;
689 }
690 Ok(())
691 }
692}
693
694pub struct BgiArchiveFile<T: Write + Seek> {
696 header: BgiFileHeader,
697 writer: Arc<Mutex<T>>,
698 pos: usize,
699 headers: Arc<Mutex<HashMap<String, BgiFileHeader>>>,
700 name: String,
701}
702
703impl<T: Write + Seek> Write for BgiArchiveFile<T> {
704 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
705 let mut writer = self.writer.lock_blocking();
706 writer.seek(SeekFrom::Start(self.header.offset as u64 + self.pos as u64))?;
707 let bytes_written = writer.write(buf)?;
708 self.pos += bytes_written;
709 self.header.size = self.header.size.max(self.pos as u32);
710 Ok(bytes_written)
711 }
712
713 fn flush(&mut self) -> std::io::Result<()> {
714 self.writer.lock_blocking().flush()
715 }
716}
717
718impl<T: Write + Seek> Seek for BgiArchiveFile<T> {
719 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
720 let new_pos = match pos {
721 SeekFrom::Start(offset) => offset as usize,
722 SeekFrom::End(offset) => {
723 if offset < 0 {
724 if (-offset) as usize > self.header.size as usize {
725 return Err(std::io::Error::new(
726 std::io::ErrorKind::InvalidInput,
727 "Seek from end exceeds file length",
728 ));
729 }
730 self.header.size as usize - (-offset) as usize
731 } else {
732 self.header.size as usize + offset as usize
733 }
734 }
735 SeekFrom::Current(offset) => {
736 if offset < 0 {
737 if (-offset) as usize > self.pos {
738 return Err(std::io::Error::new(
739 std::io::ErrorKind::InvalidInput,
740 "Seek from current exceeds current position",
741 ));
742 }
743 self.pos.saturating_sub((-offset) as usize)
744 } else {
745 self.pos + offset as usize
746 }
747 }
748 };
749 self.pos = new_pos;
750 Ok(self.pos as u64)
751 }
752}
753
754impl<T: Write + Seek> Drop for BgiArchiveFile<T> {
755 fn drop(&mut self) {
756 self.headers
757 .lock_blocking()
758 .insert(self.name.clone(), self.header.clone());
759 }
760}
761
762struct Writer<'a> {
763 inner: Box<dyn Write + 'a>,
764 mem: MemWriter,
765}
766
767impl std::fmt::Debug for Writer<'_> {
768 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
769 f.debug_struct("Writer").field("mem", &self.mem).finish()
770 }
771}
772
773impl<'a> Write for Writer<'a> {
774 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
775 self.mem.write(buf)
776 }
777
778 fn flush(&mut self) -> std::io::Result<()> {
779 self.mem.flush()
780 }
781}
782
783impl<'a> Seek for Writer<'a> {
784 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
785 self.mem.seek(pos)
786 }
787
788 fn stream_position(&mut self) -> std::io::Result<u64> {
789 self.mem.stream_position()
790 }
791
792 fn rewind(&mut self) -> std::io::Result<()> {
793 self.mem.rewind()
794 }
795}
796
797impl<'a> Drop for Writer<'a> {
798 fn drop(&mut self) {
799 let _ = self.inner.write_all(&self.mem.data);
800 let _ = self.inner.flush();
801 }
802}